#pragma once

#include <iostream>
#include <sys/epoll.h>
#include "Socket.hpp"
#include "Log.hpp"
#include "InetAddr.hpp"

using namespace socket_ns;

/*
#include <sys/epoll.h>

int epoll_create(int size);

    调用epoll_create时，内核除了帮我们在epoll文件系统里建了个file结点（epoll_create创建的文件描述符）
    在内核cache里建了个 红黑树用于存储以后epoll_ctl传来的socket外，还会再建立一个list链表，用于存储准
    备就绪的事件.（概括就是：调用epoll_create方法时，内核会跟着创建一个eventpoll对象）


    size参数现在并不起作用，只是给内核一个提示，告诉内核应该如何为内部数据结构划分初始大小。

    返回文件描述符epollfd
------------------------------------------------------------------------------------------

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

    创建epoll对象后，可以用epoll_ctl添加或删除所要监听的socket。

    返回值：
         成功返回0，不成功返回-1并设置errno

    1、epfd ：epoll_create()的返回值；
    2、op : 指定操作类型。操作类型有如下3种：
        EPOLL_CTL_ADD：注册新的fd到epfd中；
        EPOLL_CTL_MOD：修改已经注册的fd的监听事件；
        EPOLL_CTL_DEL：从epfd中删除一个fd；
    3、fd：是需要监听的fd
    4、event：告诉内核需要监听什么事，指定事件，它是epoll_event结构指针类型。
------------------------------------------------------------------------------------------
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);

    socket收到数据后，中断程序会给eventpoll的“就绪列表”添加socket引用。

    返回值：
        成功时返回就绪的文件描述符个数，失败时返回-1并设置errno。

    1、epfd ：epoll_create()的返回值；
    2、events: 用来记录被触发的events（结构参考epoll_ctl），其大小受制于maxevents
    3、maxevents: 设定最多监听多少个事件，必须大于0，一般设定为65535
    4、timeout：在函数调用中阻塞时间上限，单位是ms
    timeout = -1：表示调用将一直阻塞，直到有文件描述符进入ready状态或者捕获到信号才返回；
    timeout = 0：用于非阻塞检测是否有描述符处于ready状态，不管结果怎么样，调用都立即返回；
    timeout > 0：表示调用将最多持续timeout时间，如果期间有检测对象变为ready状态或者捕获到信号则返回，否则直到超时。
*/

class EpollServer
{
    const static int size = 128;
    const static int num = 128;

public:
    EpollServer(uint16_t port)
        : _port(port), _listensock(std::make_unique<TcpSocket>())
    {
        _listensock->BuildListenSocket(_port);
        _epfd = epoll_create(size);
        if (_epfd < 0)
        {
            LOG(FATAL, "epoll_create error!\n");
            exit(1);
        }
        LOG(INFO, "epoll create success, epfd: %d\n", _epfd);
    }

    void InitServer()
    {
        struct epoll_event ev;
        ev.events = EPOLLIN;
        ev.data.fd = _listensock->Sockfd();
        int n = ::epoll_ctl(_epfd, EPOLL_CTL_ADD, _listensock->Sockfd(), &ev);
        if (n < 0)
        {
            LOG(FATAL, "epoll_ctl error!\n");
            exit(2);
        }
        LOG(INFO, "epoll_ctl success, add new sockfd : %d\n", _listensock->Sockfd());
    }

    // 处理新连接
    void Accepter()
    {
        InetAddr addr;
        SockSPtr sockfd = _listensock->Accepter(&addr);
        if (sockfd->Sockfd() < 0)
        {
            LOG(ERROR, "get new link failed\n");
            return;
        }
        LOG(DEBUG, "get a new link, client info %s:%d\n", addr.Ip().c_str(), addr.port());
        struct epoll_event ev;
        ev.data.fd = sockfd->Sockfd();
        ev.events = EPOLLIN;
        ::epoll_ctl(_epfd, EPOLL_CTL_ADD, sockfd->Sockfd(), &ev);
        LOG(INFO, "epoll_ctl success, add new sockfd : %d\n", sockfd);
    }

    // 处理普通的就绪fd
    void HandlerIO(int fd)
    {
        char inbuffer[1024];
        size_t n = ::recv(fd, inbuffer, sizeof(inbuffer) - 1, 0);
        if (n > 0)
        {
            inbuffer[n] = 0;
            std::cout << "client say# " << inbuffer << std::endl;
            std::string content = "<html><body><h1>test selectserver</h1></body></html>";
            std::string echo_str = "HTTP/1.0 200 OK\r\n";
            echo_str += "Content-Type: text/html\r\n";
            echo_str += "Content-Length: " + std::to_string(content.size()) + "\r\n\r\n";
            echo_str += content;
            ::send(fd, echo_str.c_str(), echo_str.size(), 0);
        }
        else if (n == 0)
        {
            LOG(INFO, "client quit...\n");
            // 取消fd的事件关系
            ::epoll_ctl(_epfd, EPOLL_CTL_DEL, fd, nullptr);
            ::close(fd);
        }
        else
        {
            LOG(ERROR, "recv error\n");
            // 取消fd的事件关系
            ::epoll_ctl(_epfd, EPOLL_CTL_DEL, fd, nullptr);
            ::close(fd);
        }
    }

    void HandlerEvent(int n)
    {
        // 事件派发
        for (int i = 0; i < n; i++)
        {
            int fd = revs[i].data.fd;
            uint32_t revents = revs[i].events;
            LOG(INFO, "%d 上面有事件就绪了,具体事件是: %s\n", fd, EventsToString(revents).c_str());

            // 读事件是否就绪
            if (revents & EPOLLIN)
            {
                if (fd == _listensock->Sockfd())
                    Accepter();
                else
                    HandlerIO(fd);
            }
        }
    }

    void Loop()
    {
        int timeout = -1;
        while (true)
        {
            int n = ::epoll_wait(_epfd, revs, num, timeout);
            switch (n)
            {
            case 0:
                LOG(DEBUG, "epoll_wait time out\n");
                break;
            case -1:
                LOG(ERROR, "epoll_wait error\n");
                break;
            default:
                LOG(INFO, "haved event ready, n : %d\n", n);
                // 进行任务派发
                HandlerEvent(n);
                sleep(1);
                break;
            }
        }
    }

    std::string EventsToString(uint32_t events)
    {
        std::string eventstr;
        if (events & EPOLLIN)
            eventstr = "EPOLLIN";
        if (events & EPOLLOUT)
            eventstr += "|EPOLLOUT";
        return eventstr;
    }

    ~EpollServer()
    {
    }

private:
    uint16_t _port;
    std::unique_ptr<Socket> _listensock;
    int _epfd;
    struct epoll_event revs[num];
};